package com.example.demo.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.config.WXConfig;
import com.example.demo.utils.model.*;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * 微信支付核心工具
 *
 * @author xueyj
 * @date 9:57 2021/1/6
 */
@Slf4j
@Component
public class WxPayV3Util {

    @Autowired
    private WXConfig wxConfig;

    /**
     * 读取私钥
     */
    public PrivateKey getPrivateKey() {

        String content = wxConfig.getPRIVATEKEY();

        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");

            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 私钥签名
     */
    public String privateSign(String sign6) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        PrivateKey privateKey = getPrivateKey();

        Signature sign = Signature.getInstance("SHA256withRSA");

        sign.initSign(privateKey);

        sign.update(sign6.getBytes(StandardCharsets.UTF_8));

        return Base64.getEncoder().encodeToString(sign.sign());

    }

    /**
     * 配置请求头的签名
     */
    public CloseableHttpClient wxClient() {
        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(
                        wxConfig.getPRIVATEKEY().getBytes(StandardCharsets.UTF_8)));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3秘钥）
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(wxConfig.getMAC_HID(),
                        new PrivateKeySigner(wxConfig.getSERIAL_NO(),
                                merchantPrivateKey)), wxConfig.getAPI3().getBytes(StandardCharsets.UTF_8));

        // 初始化httpClient
        return WechatPayHttpClientBuilder.create()
                .withMerchant(wxConfig.getMAC_HID(), wxConfig.getSERIAL_NO(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    /**
     * 读取支付回调数据的报文
     */
    public StringBuilder readNotifyMessage(HttpServletRequest request) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb;
    }

    /**
     * 解密支付回调数据
     */
    public NotifyBody decryptNotifyMessage(StringBuilder sb) throws GeneralSecurityException, IOException {
        WxResponseBody wxResponseBody = JSONObject.parseObject(sb.toString(), WxResponseBody.class);

        /*加密数据所在地*/
        Resource resource = wxResponseBody.getResource();

        AesUtil aesUtil = new AesUtil(wxConfig.getAPI3().getBytes(StandardCharsets.UTF_8));

        String s = aesUtil.decryptToString(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8),
                resource.getNonce().getBytes(StandardCharsets.UTF_8),
                resource.getCiphertext());

        return JSONObject.parseObject(s, NotifyBody.class);
    }

    /**
     * 判断是不是成功回调
     */
    public Boolean checkNotifyBody(NotifyBody notifyBody) {

        if (!(notifyBody.getAppid() != null && notifyBody.getAppid().equals(wxConfig.getAPP_ID()))
        ) {
            return false;
        }

        if (!(notifyBody.getMchid() != null && notifyBody.getMchid().equals(wxConfig.getMAC_HID()))) {
            return false;
        }

        return notifyBody.getTrade_state() != null && "SUCCESS".equals(notifyBody.getTrade_state());
    }

    /**
     * 设置必须请求头
     */
    public HttpPost postHeader(HttpPost httpPost) {
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    public HttpGet postHeader(HttpGet httpPost) {
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    /**
     * 读取平台证书的公钥
     */
    public PublicKey getPtzPub() throws CertificateException, IOException {
        /*这里可以写到配置文件*/
        String pTZCERT = "-----BEGIN CERTIFICATE-----\n" +
                "MIID3DCCAsSgAwIBAgIUeMQsmARXdFE+malg0ToqvW6Iu4kwDQYJKoZIhvcNAQEL\n" +
                "BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT\n" +
                "FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg\n" +
                "Q0EwHhcNMjEwMTAzMDUxNTQ2WhcNMjYwMTAyMDUxNTQ2WjBuMRgwFgYDVQQDDA9U\n" +
                "ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl\n" +
                "bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo\n" +
                "ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSp9XwjMpiWQftyK8P\n" +
                "1o6S+9azN96nmGoCytgEPSQgJwo4bgkzcDQnoxCmBOLy7br2okk/pO7eNuyAx3Hj\n" +
                "JUX0jAFlUQ9/8U7f+xog07bElM9cKWY6I5UloljUNcLWz4KoSVt9AMvhdNbi2Q/H\n" +
                "ORVBNLnuIQ8kxF4/UHo1LzPveSsQA1QDRAfa7kPIkQQawFVg94fsw4ApizevUhNy\n" +
                "22IUwXsi6L2EnnS1Xt0Q22kCwmwlKhTgH6WGZVaU6FaNY9mc7ir2RfP039YX8+O0\n" +
                "/XFpNOrHx1ySx1xwSK9y5FJHOBBzigVkXi4RCgSqz2l0rRotBtt9LlHonkh4EvHE\n" +
                "/tAJAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBc\n" +
                "MFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js\n" +
                "P0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ\n" +
                "KoZIhvcNAQELBQADggEBAHPns6OTZExZ11VGLEp8xDdNE9k1HEYT1S4seCXRlUJu\n" +
                "Q2VLMMl1zw1pU6ZKKtkI0bU6vGCobKicXNbti9WI6TZtzSAMnWeOwkRZ+rg4DDdh\n" +
                "gJ5eYj4YK8p/+EFQU4oXVhRXkXKl7dHfZQfbliI4/2AlogywyrMIDAr9A3GxFkxw\n" +
                "CsFzQ6RofIrHhJovvn8sftDLxaYPgTgjcyM6csl3z+dZXegxw8XqR+a4agT3OSRI\n" +
                "ymcC8WD13m5YlodJBAr/WvQnlxiKUL78yfSSWQf2GiJShnnQm7Tb5uL4njMF7ncW\n" +
                "zZF6amz3HJaVEly2PHUAQX0Ubr+lj3B5O6t+C4fOzbs=\n" +
                "-----END CERTIFICATE-----";
        byte[] bytes = pTZCERT.getBytes(StandardCharsets.UTF_8);
        ByteArrayInputStream basis = new ByteArrayInputStream(bytes);
        CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");

        X509Certificate cert = null;
        try {
            cert = (X509Certificate) certificatefactory.generateCertificate(basis);
        } catch (CertificateException e) {
            log.error(e.toString());
        }
        assert cert != null;
        PublicKey publicKey = cert.getPublicKey();
        basis.close();
        return publicKey;
    }

    /**
     * 验证微信身份
     */
    public boolean verify(String srcData, String signedData, PublicKey publicKey) {
        if (srcData == null || signedData == null || publicKey == null) {
            return false;
        }
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(publicKey);
            sign.update(srcData.getBytes(StandardCharsets.UTF_8));
            return sign.verify(Base64.getDecoder().decode(signedData));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 组装请求body
     */
    public String mustUniFiledStr(TProduct tProduct, String orderCode, String openId) {
        BigDecimal multiply = tProduct.getAmount().multiply(new BigDecimal(100));
        // 下单成功 获得perid
        MustUniFiedOrderBody mustUniFiedOrderBody =
                MustUniFiedOrderBody.builder()
                        .appid(wxConfig.getAPP_ID())
                        .mchid(wxConfig.getMAC_HID())
                        .description(tProduct.getProductName())
                        .notify_url(wxConfig.getNOTIFY_URL())
                        .out_trade_no(orderCode)
                        .payer(new WxPayer(openId))
                        .amount(new WxAmount(multiply.intValue()))
                        .build();
        return JSON.toJSONString(mustUniFiedOrderBody);
    }

    public HttpPost getPostHttp() {
        return postHeader(new HttpPost(wxConfig.getJSPI_URL()));
    }

    public String getAppId() {
        return wxConfig.getAPP_ID();
    }
}
